﻿#region 信息

// Copyright©2014 Rex Rao all rights reserved.
// File name : SoMcCoffee\Sohobloo.SoMcCoffee\QueryExtension.cs
// Author : Sohobloo
// Create time : 2014/02/05 6:36
// Update time : 2014/02/05 8:50

#endregion

#region 引用

using System;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using Sohobloo.SoCommon;
using Sohobloo.SoCommon.CommonExtension;

#endregion

namespace Sohobloo.SoMcCoffee.Extension
{
    public static class QueryExtensions
    {
        /// <summary>
        ///     Include extension method for IQueryable
        /// </summary>
        /// <typeparam name="TEntity">Type of elements in IQueryable</typeparam>
        /// <param name="source">Queryable object</param>
        /// <param name="path">Expression with path to include</param>
        /// <returns>Queryable object with include path information</returns>
        public static IQueryable<TEntity> Include<TEntity>(this IQueryable<TEntity> source,
            Expression<Func<TEntity, object>> path)
            where TEntity : class
        {
            return source.Include(AnalyzeExpressionPath(path));
        }

        /// <summary>
        ///     Paginate query in a specific page range
        /// </summary>
        /// <typeparam name="TEntity">Typeof entity in underlying query</typeparam>
        /// <param name="source">Query to paginate</param>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageSize">Page size</param>
        /// <param name="recordCount"></param>
        /// <returns>A paged queryable</returns>
        public static IQueryable<TEntity> Paginate<TEntity>(this IOrderedQueryable<TEntity> source, ref int pageIndex,
            int pageSize, out int recordCount)
            where TEntity : class
        {
            ArgumentHelper.Instance
                          .CheckNull(source)
                          .SetRange(ref pageSize, 1, RangeRestriction.Min)
                          .SetRange(ref pageIndex, 1, (recordCount = source.Count()) / pageSize);

            return source.Skip(((pageIndex - 1) * pageSize)).Take(pageSize);
        }

        #region Private Methods

        private static string AnalyzeExpressionPath<TEntity, TProperty>(Expression<Func<TEntity, TProperty>> expression)
            where TEntity : class
        {
            ArgumentHelper.Instance.CheckNull(expression);

            var body = expression.Body as MemberExpression;
            if(body.IsNull()
               || !body.Member.DeclaringType.IsAssignableFrom(typeof(TEntity))
               || body.Expression.NodeType != ExpressionType.Parameter)
            {
                throw new ArgumentException("expression");
            }

            return body.Member.Name;
        }

        #endregion
    }
}